home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’90 / Grabber ƒ / sources ƒ / grabber_INIT.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-15  |  20.2 KB  |  1,014 lines  |  [TEXT/KAHL]

  1.  
  2. /*
  3.  *  grabber_INIT.c
  4.  *
  5.  *  Copyright © 1990 Michael Kahl.
  6.  *
  7.  */
  8.  
  9. #include <asm.h>
  10. #include <Color.h>
  11. #include <VRetraceMgr.h>
  12.  
  13. #include "options.h"
  14.  
  15.  
  16.     /*  trap/lo-mem intercepts  */
  17.  
  18. struct intercept {
  19.     short                jsr;
  20.     void                *new;
  21.     short                jmp;
  22.     void                *old;
  23. };
  24.  
  25. static void intercept_trap(short, struct intercept *, void *);
  26. static void intercept_lomem(short, struct intercept *, void *);
  27.  
  28. static void myGNEFilter(void);
  29. static void myTestControl(void);
  30. static void myTrackControl(void);
  31. static void myHiliteControl(void);
  32. static void myInitCursor(void);
  33. static void mySetCursor(void);
  34. static void myWaitMouseUp(void);
  35. static void myGetMouse(void);
  36. static void myScrollRect(void);
  37. static void myGetNextEvent(void);
  38.  
  39. static struct intercept iJGNE, iTestControl, iTrackControl, iHiliteControl;
  40. static struct intercept iInitCursor, iSetCursor;
  41. static struct intercept iWaitMouseUp, iGetMouse, iScrollRect, iGetNextEvent;
  42.  
  43.  
  44.     /*  other routines  */
  45.     
  46. static void install_intercepts(void);
  47. static void get_options(void);
  48. static void install_VBL(void);
  49.  
  50. static void filterClick(EventRecord *, char *);
  51. static void trackMouse(void);
  52. static void filterScroll(Point *, Rect *);
  53. static void getEvent(EventRecord *);
  54.  
  55. static Boolean isScrollBar(ControlHandle);
  56. static void localize(Point *, WindowPtr);
  57. static void globalize(Point *, WindowPtr);
  58. static void GetA4(void);
  59.  
  60.  
  61.     /*  global variables  */
  62.  
  63. static ControlHandle theBar, vBar, hBar;
  64. static short partCode, deltaMax, deltaMin, delta;
  65. static Boolean triggered, tracking, detached, bounced;
  66. static Point thePoint, syncPoint, accum, zero;
  67.  
  68. static struct opt opt = { 1, 0, 0, 0, 0, 1, 1, 1 };
  69.  
  70.  
  71.     /*  hand cursor  */
  72.  
  73. static Cursor theGrabber = {
  74.     0x0180, 0x1A70, 0x2648, 0x264A, 0x124D, 0x1249, 0x6809, 0x9801,
  75.     0x8802, 0x4002, 0x2002, 0x2004, 0x1004, 0x0808, 0x0408, 0x0408,
  76.     0x0180, 0x1BF0, 0x3FF8, 0x3FFA, 0x1FFF, 0x1FFF, 0x6FFF, 0xFFFF,
  77.     0xFFFE, 0x7FFE, 0x3FFE, 0x3FFC, 0x1FFC, 0x0FF8, 0x07F8, 0x07F8,
  78.     9, 8
  79. };
  80.  
  81.  
  82.     /*  macros  */
  83.  
  84. #define evWhat            OFFSET(EventRecord,what)
  85. #define evModifiers        OFFSET(EventRecord,modifiers)
  86.  
  87. #define INTERVAL        3
  88. #define MAXRATE            200
  89. #define FRICTION        2
  90.  
  91.  
  92. /* ---------- installation ---------- */
  93.  
  94.  
  95. void
  96. main(void)
  97. {
  98.     asm {
  99.         move.l    a4,-(sp)
  100.         movea.l    a0,a4
  101.         _RecoverHandle
  102.         move.l    a0,-(sp)
  103.         _DetachResource
  104.         jsr        GetA4
  105.         move.l    a4,(a0)
  106.         jsr        install_intercepts
  107.         jsr        install_VBL
  108.         jsr        get_options
  109.         movea.l    (sp)+,a4
  110.     }
  111. }
  112.  
  113.  
  114. /*
  115.  *  install_intercepts
  116.  *
  117.  */
  118.  
  119. static void
  120. install_intercepts(void)
  121. {
  122.     intercept_lomem(0x29A, &iJGNE, myGNEFilter);
  123.     intercept_trap(0xA966, &iTestControl, myTestControl);
  124.     intercept_trap(0xA968, &iTrackControl, myTrackControl);
  125.     intercept_trap(0xA95D, &iHiliteControl, myHiliteControl);
  126.     intercept_trap(0xA850, &iInitCursor, myInitCursor);
  127.     intercept_trap(0xA851, &iSetCursor, mySetCursor);
  128.     intercept_trap(0xA977, &iWaitMouseUp, myWaitMouseUp);
  129.     intercept_trap(0xA972, &iGetMouse, myGetMouse);
  130.     intercept_trap(0xA8EF, &iScrollRect, myScrollRect);
  131.     intercept_trap(0xA970, &iGetNextEvent, myGetNextEvent);
  132. }
  133.  
  134.  
  135. /*
  136.  *  get_options
  137.  *
  138.  */
  139.  
  140. static void
  141. get_options(void)
  142. {
  143.     Handle r;
  144.     
  145.     if (r = Get1Resource('cnfg', 0))
  146.         opt = * (struct opt *) *r;
  147. }
  148.  
  149.  
  150. /* ---------- jGNEFilter intercept ---------- */
  151.  
  152.  
  153. /*
  154.  *  myGNEFilter
  155.  *
  156.  */
  157.  
  158. static void
  159. myGNEFilter(void)
  160. {
  161.         /*  see if we want event  */
  162.  
  163.     asm {
  164.         tst.b    8(sp)
  165.         beq.s    @2                        ;  event already processed
  166.         cmpi.w    #mouseDown,evWhat(a1)
  167.         bne.s    @2                        ;  not a mouse-down event
  168.     }
  169.     
  170.         /*  set up registers  */
  171.  
  172.     asm {
  173.         movem.l    d0-d2/a0-a1/a4,-(sp)
  174.         jsr        GetA4
  175.         movea.l    (a0),a4
  176.     }
  177.     
  178.         /*  see if we're enabled  */
  179.     
  180.     if (!opt.enabled)
  181.         goto done;
  182.     
  183.         /*  see if we want event  */
  184.  
  185.     asm {
  186.         move.b    evModifiers(a1),d0
  187.         cmp.b    opt,d0
  188.         bne.s    @1                        ;  wrong magic keys
  189.     }
  190.     
  191.         /*  process click  */
  192.  
  193.     asm {
  194.         pea        32(sp)                    ;  arg: ==> result
  195.         move.l    a1,-(sp)                ;  arg: ==> event
  196.         jsr        filterClick
  197.         addq.l    #8,sp
  198.     }
  199.     
  200.         /*  done  */
  201.  
  202. done:
  203.     asm {
  204. @1        movem.l    (sp)+,d0-d2/a0-a1/a4
  205. @2    }
  206. }
  207.  
  208.  
  209. /*
  210.  *  filterClick - process click at jGNEFilter time
  211.  *
  212.  */
  213.  
  214. static void
  215. filterClick(register EventRecord *event, char *result)
  216. {
  217.     Point where = event->where;
  218.     WindowPeek wp;
  219.     int which = FindWindow(where, &wp), refnum;
  220.     ControlHandle cH;
  221.     Rect box;
  222.  
  223.         /*  make sure click is in content region of front window  */
  224.  
  225.     if (wp) {
  226.         if ((WindowPtr) wp != FrontWindow())
  227.             return;
  228.         if (which == inSysWindow) {
  229.             refnum = wp->windowKind;
  230.             wp->windowKind = userKind;
  231.             which = FindWindow(where, &wp);
  232.             wp->windowKind = refnum;
  233.         }
  234.     }
  235.     if (which != inContent)
  236.         return;
  237.     localize(&where, (WindowPtr) wp);
  238.         
  239.         /*  search window's control list for applicable scroll bars  */
  240.  
  241.     vBar = hBar = 0;
  242.     for (cH = wp->controlList; cH; cH = (**cH).nextControl) {
  243.         if (!isScrollBar(cH))
  244.             continue;
  245.         box = (**cH).contrlRect;
  246.         if (box.bottom - box.top > box.right - box.left) {
  247.             /* vertical */
  248.             if (!vBar || box.left < (**vBar).contrlRect.left) {
  249.                 box.right = box.left;
  250.                 box.left = 0;
  251.                 if (PtInRect(where, &box))
  252.                     vBar = cH;
  253.             }
  254.         }
  255.         else {
  256.             /* horizontal */
  257.             if (!hBar || box.top < (**hBar).contrlRect.top) {
  258.                 box.bottom = box.top;
  259.                 box.top = 0;
  260.                 if (PtInRect(where, &box))
  261.                     hBar = cH;
  262.             }
  263.         }
  264.     }
  265.     if (!vBar && !hBar)
  266.         return;            /*  no applicable scroll bars  */
  267.  
  268.         /*  track click in scrollable area  */
  269.  
  270.     SetCursor(&theGrabber);
  271.     syncPoint = event->where;
  272.     triggered = true;
  273.     tracking = false;
  274.     partCode = delta = 0;
  275.     deltaMax = 2;
  276.     deltaMin = -2;
  277.     accum = zero;
  278.     do {
  279.         if (!WaitMouseUp()) {
  280.             *result = 0;
  281.             event->what = nullEvent;    /*  just to make sure  */
  282.             return;
  283.         }
  284.     } while (!partCode);
  285.     
  286.         /*  report click in scroll bar  */
  287.  
  288.     event->where = topLeft((**theBar).contrlRect);
  289.     event->where.v += 2;
  290.     event->where.h += 2;
  291.     globalize(&event->where, (WindowPtr) wp);
  292.     thePoint = event->where;
  293. }
  294.  
  295.  
  296. /* ---------- TestControl intercept ---------- */
  297.  
  298.  
  299. /*
  300.  *  myTestControl
  301.  *
  302.  *  While "grabbing", we return the value of "partCode" regardless.  The
  303.  *  mouse tracking code requests a scroll by setting "partCode" according
  304.  *  to the desired scroll direction.  When a scroll is not desired, "partCode"
  305.  *  can be set to zero, simulating the mouse being moved outside the scroll
  306.  *  arrow.
  307.  *
  308.  */
  309.  
  310. void
  311. myTestControl(void)
  312. {
  313.     asm {
  314.         jsr        GetA4
  315.         move.l    a4,-(sp)
  316.         movea.l    (a0),a4
  317.         tst.b    triggered
  318.         beq.s    @1                    ;  not activated
  319.         move.l    16(sp),d0
  320.         cmp.l    theBar,d0
  321.         bne.s    @1                    ;  wrong control
  322. ;
  323.         move.w    partCode,20(sp)        ;  "lie" about where in the control we clicked
  324.         movea.l    (sp)+,a4
  325.         addq.l    #4,sp
  326.         movea.l    (sp)+,a0
  327.         addq.l    #8,sp
  328.         jmp        (a0)                ;  return directly to caller
  329. ;
  330. @1        movea.l    (sp)+,a4            ;  chain on to _TestControl
  331.     }
  332. }
  333.  
  334.  
  335. /* ---------- TrackControl intercept ---------- */
  336.  
  337.  
  338. /*
  339.  *  myTrackControl
  340.  *
  341.  *  All this one does is set a flag so we know when we're inside
  342.  *  _TrackControl.
  343.  *
  344.  */
  345.  
  346. void
  347. myTrackControl(void)
  348. {
  349.     asm {
  350.         jsr        GetA4
  351.         move.l    a4,-(sp)
  352.         movea.l    (a0),a4
  353.         tst.b    triggered
  354.         beq.s    @1
  355.         st        tracking
  356. @1        movea.l    (sp)+,a4
  357.     }
  358. }
  359.  
  360.  
  361. /* ---------- HiliteControl intercept ---------- */
  362.  
  363.  
  364. /*
  365.  *  myHiliteControl
  366.  *
  367.  *  This intercept is for cosmetic purposes only.  While "grabbing",
  368.  *  we don't want the arrows of the scroll bar to hilite.
  369.  *
  370.  */
  371.  
  372. void
  373. myHiliteControl(void)
  374. {
  375.     asm {
  376.         jsr        GetA4
  377.         move.l    a4,-(sp)
  378.         movea.l    (a0),a4
  379.         tst.b    triggered
  380.         beq.s    @1
  381.         move.l    14(sp),d0
  382.         cmp.l    theBar,d0
  383.         bne.s    @1
  384.         clr.w    12(sp)                ;  set partCode arg to 0 (no hiliting)
  385. @1        movea.l    (sp)+,a4
  386.     }
  387. }
  388.  
  389.  
  390. /* ---------- cursor intercepts ---------- */
  391.  
  392.  
  393. /*
  394.  *  myInitCursor
  395.  *
  396.  *  This intercept is for cosmetic purposes only.  While "grabbing", we don't
  397.  *  want the application to disrupt the hand cursor.
  398.  *
  399.  */
  400.  
  401. void
  402. myInitCursor(void)
  403. {
  404.     asm {
  405.         jsr        GetA4
  406.         move.l    a4,-(sp)
  407.         movea.l    (a0),a4
  408.         tst.b    triggered
  409.         movea.l    (sp)+,a4
  410.         beq.s    @1
  411.         addq.l    #4,sp                ;  don't chain on to _InitCursor
  412. @1    }
  413. }
  414.  
  415.  
  416. /*
  417.  *  mySetCursor
  418.  *
  419.  *  This intercept is for cosmetic purposes only.  While "grabbing", we don't
  420.  *  want the application to disrupt the hand cursor.
  421.  *
  422.  *  If we were really paranoid, we should intercept _SetCCursor as well.  But
  423.  *  in practice, we only need to worry about "InitCursor()" or "SetCursor(&arrow)".
  424.  *
  425.  */
  426.  
  427. void
  428. mySetCursor(void)
  429. {
  430.     asm {
  431.         jsr        GetA4
  432.         move.l    a4,-(sp)
  433.         movea.l    (a0),a4
  434.         tst.b    triggered
  435.         movea.l    (sp)+,a4
  436.         beq.s    @1
  437.         addq.l    #4,sp                ;  don't chain on to _SetCursor
  438.         move.l    (sp)+,(sp)
  439. @1    }
  440. }
  441.  
  442.  
  443. /* ---------- GetMouse intercept ---------- */
  444.  
  445.  
  446. /*
  447.  *  myGetMouse
  448.  *
  449.  *  While "grabbing", we lie and say the mouse is in the scroll bar.
  450.  *
  451.  */
  452.  
  453. void
  454. myGetMouse(void)
  455. {
  456.     asm {
  457.         jsr        GetA4
  458.         move.l    a4,-(sp)
  459.         movea.l    (a0),a4
  460.         tst.b    triggered
  461.         beq.s    @1
  462. ;
  463.         movea.l    12(sp),a0
  464.         move.l    thePoint,(a0)
  465.         move.l    a0,-(sp)
  466.         _GlobalToLocal
  467.         movea.l    (sp)+,a4
  468.         addq.l    #4,sp                    ;  return directly to caller
  469.         move.l    (sp)+,(sp)
  470.         rts
  471. ;
  472. @1        movea.l    (sp)+,a4                ;  chain on to real _GetMouse
  473.     }
  474. }
  475.  
  476.  
  477. /* ---------- ScrollRect intercept ---------- */
  478.  
  479.  
  480. /*
  481.  *  myScrollRect
  482.  *
  483.  *  Most of the work is done by "filterScroll", below.
  484.  *
  485.  *  If no scroll is requested (either naturally or as a result of
  486.  *  "filterScroll"), we set the revealed region to the empty region
  487.  *  and do not chain on to _ScrollRect.
  488.  *
  489.  */
  490.  
  491. void
  492. myScrollRect(void)
  493. {
  494.     asm {
  495.         jsr        GetA4
  496.         move.l    a4,-(sp)
  497.         movea.l    (a0),a4
  498.         tst.b    tracking
  499.         beq.s    @1
  500. ;
  501.         move.l    20(sp),-(sp)            ;  arg: ==> rect
  502.         pea        20(sp)                    ;  arg: ==> dv/dh
  503.         jsr        filterScroll
  504.         addq.l    #8,sp
  505.         tst.l    16(sp)
  506.         bne.s    @1
  507. ;
  508.         move.l    12(sp),-(sp)
  509.         _SetEmptyRgn
  510.         movea.l    (sp)+,a4
  511.         addq.l    #4,sp
  512.         movea.l    (sp)+,a0
  513.         lea        12(sp),sp                ;  return directly to caller
  514.         jmp        (a0)
  515. ;
  516. @1        movea.l    (sp)+,a4                ;  chain on to _ScrollRect
  517.     }
  518. }
  519.  
  520.  
  521. /*
  522.  *  filterScroll - process scroll request at _ScrollRect time
  523.  *
  524.  *  To enable scrolling faster than the application actually permits,
  525.  *  we "accumulate" scroll requests whenever the actual scroll does
  526.  *  not catch up to the requested scroll.
  527.  *
  528.  *  In this routine we also adjust deltaMin/deltaMax to reflect the
  529.  *  perceived scroll increment.
  530.  *
  531.  */
  532.  
  533. static void
  534. filterScroll(register Point *d, register Rect *r)
  535. {
  536.     register short ofs = d->v | d->h;
  537.  
  538.     if (ofs) {
  539.         partCode = 0;
  540.         accum.v += d->v;
  541.         accum.h += d->h;
  542.         if (ofs > deltaMax - deltaMin + 1)
  543.             deltaMin = deltaMax + 1 - ofs;
  544.         else if (ofs < deltaMin - deltaMax - 1)
  545.             deltaMax = deltaMin - 1 - ofs;
  546.         delta -= ofs;
  547.         if (delta < deltaMin || delta > deltaMax) {
  548.             ofs = accum.v | accum.h;
  549.             if (ofs < 0)
  550.                 ofs = -ofs;
  551.             if (vBar && ofs < r->bottom - r->top || hBar && ofs < r->right - r->left) {
  552.                 *d = zero;
  553.                 return;        /*  let it accumulate  */
  554.             }
  555.         }
  556.         syncPoint.v += accum.v;
  557.         syncPoint.h += accum.h;
  558.         *d = accum;
  559.         accum = zero;
  560.     }
  561. }
  562.  
  563.  
  564. /* ---------- WaitMouseUp intercept ---------- */
  565.  
  566.  
  567. /*
  568.  *  myWaitMouseUp
  569.  *
  570.  *  Most of the work is done by "trackMouse", below.
  571.  *
  572.  *  We report the mouse is down as long as we're "grabbing", even if the
  573.  *  mouse has actually been released (this is the momentum feature).
  574.  *
  575.  *  However, if we need to reverse directions, we have to report that the
  576.  *  mouse button has been released, even though it's still down.  (In this
  577.  *  case, a click in the other scroll arrow is simulated.)
  578.  *
  579.  *  Note that D3 is a local variable in _TrackControl that stores the
  580.  *  partCode corresponding to the scroll direction.
  581.  *
  582.  */
  583.  
  584. void
  585. myWaitMouseUp(void)
  586. {
  587.     asm {
  588.         jsr        GetA4
  589.         move.l    a4,-(sp)
  590.         movea.l    (a0),a4
  591.         tst.b    triggered
  592.         beq.s    @1
  593.         jsr        trackMouse
  594.         move.b    tracking,d0
  595.         beq.s    @0
  596.         move.w    partCode,d0
  597. @0        move.b    triggered,12(sp)
  598. @1        movea.l    (sp)+,a4
  599.         beq.s    @2
  600.         addq.l    #4,sp                ;  don't chain on to real _WaitMouseUp
  601.         tst.b    d0
  602.         beq.s    @2
  603.         cmp.b    d0,d3
  604.         beq.s    @2
  605.         sf        4(sp)
  606. @2    }
  607. }
  608.  
  609.  
  610. /*
  611.  *  trackMouse - WaitMouseUp processing
  612.  *
  613.  *  If the previous scroll request was not honored, we have to assume we've
  614.  *  bumped up against the beginning or end of the document.  In this case,
  615.  *  we need to adjust the syncPoint to the current mouse position.  If there
  616.  *  is an accumulated scroll, then we've attempted to scroll past the edge, so
  617.  *  we have to "bounce".  By scrolling one increment in the other direction and
  618.  *  back again, we get the application to call _ScrollRect again so we can
  619.  *  dispose of the accumulated scroll!
  620.  *
  621.  *  If the mouse has moved outside the region corresponding to the current
  622.  *  position, we generate a new scroll request.
  623.  *
  624.  */
  625.  
  626. static void
  627. trackMouse(void)
  628. {
  629.     static EventRecord event;
  630.  
  631.         /*  see if previous scroll request was skipped  */
  632.  
  633.     if (partCode) {
  634.         if (partCode == inDownButton && GetCtlValue(theBar) < GetCtlMax(theBar))
  635.             return;
  636.         if (partCode == inUpButton && GetCtlValue(theBar) > GetCtlMin(theBar))
  637.             return;
  638.         delta = 0;
  639.         if (accum.v | accum.h) {
  640.             partCode ^= 1;        /*  reverse direction  */
  641.             return;
  642.         }
  643.         bounced = true;
  644.         syncPoint = event.where;
  645.         partCode = 0;
  646.     }
  647.     
  648.         /*  see if mouse has moved sufficiently  */
  649.  
  650.     if (delta >= deltaMin && delta <= deltaMax) {
  651.         getEvent(&event);
  652.         if (!triggered) {
  653.             tracking = false;
  654.             InitCursor();
  655.             return;        /*  mouse is no longer down  */
  656.         }
  657.         if (vBar) {
  658.             delta = event.where.v - syncPoint.v;
  659.             if (delta > deltaMax || delta < deltaMin) {
  660.                 theBar = vBar;
  661.                 hBar = 0;
  662.             }
  663.         }
  664.         if (hBar) {
  665.             delta = event.where.h - syncPoint.h;
  666.             if (delta > deltaMax || delta < deltaMin) {
  667.                 theBar = hBar;
  668.                 vBar = 0;
  669.             }
  670.         }
  671.     }
  672.     
  673.         /*  request scroll  */
  674.  
  675.     if (delta > deltaMax)
  676.         partCode = inUpButton;
  677.     else if (delta < deltaMin)
  678.         partCode = inDownButton;
  679. }
  680.  
  681.  
  682. /*
  683.  *  getEvent
  684.  *
  685.  *  This routine fills in an event record based on the actual mouse position,
  686.  *  or the virtual mouse position if momentum has kicked in.
  687.  *
  688.  *  While the mouse is still down, this routine tracks the rate at which the
  689.  *  mouse has been moving, for subsequent use in the momentum calculation.
  690.  *
  691.  *  When the mouse goes up suddenly, momentum kicks in and the virtual mouse
  692.  *  position is detached from the actual position.  Subsequently the virtual
  693.  *  position is computed based on the speed the mouse was moving when it went
  694.  *  up, as degraded by friction.
  695.  *
  696.  */
  697.  
  698. static void
  699. getEvent(register EventRecord *event)
  700. {
  701.     static EventRecord lastEvent;
  702.     static long rate;
  703.     short d;
  704.  
  705.         /*  initial call from "filterClick"  */
  706.  
  707.     if (!tracking) {
  708.         if (OSEventAvail(mUpMask|mDownMask, event) || (event->modifiers & btnState))
  709.             triggered = false;
  710.         detached = bounced = false;
  711.         rate = 0;
  712.         lastEvent.when = 0;
  713.     }
  714.     
  715.         /*  normal call from _TrackControl  */
  716.  
  717.     else if (!detached) {
  718.         if (OSEventAvail(mUpMask|mDownMask, event) || (event->modifiers & btnState)) {
  719.             if (rate && opt.momentum)
  720.                 detached = true;
  721.             else
  722.                 triggered = false;
  723.         }
  724.         else {
  725.             if (event->when >= lastEvent.when + INTERVAL)
  726.                 rate = 0;
  727.             if (vBar && event->where.v != lastEvent.where.v) {
  728.                 rate = 100L * (event->where.v - lastEvent.where.v);
  729.                 if (event->when > lastEvent.when)
  730.                     rate /= event->when - lastEvent.when;
  731.             }
  732.             else if (hBar && event->where.h != lastEvent.where.h) {
  733.                 rate = 100L * (event->where.h - lastEvent.where.h);
  734.                 if (event->when > lastEvent.when)
  735.                     rate /= event->when - lastEvent.when;
  736.             }
  737.             if (rate > MAXRATE)
  738.                 rate = MAXRATE;
  739.             else if (rate < -MAXRATE)
  740.                 rate = -MAXRATE;
  741.         }
  742.     }
  743.     
  744.         /*  momentum  */
  745.  
  746.     if (detached) {
  747.         if (bounced || OSEventAvail(mDownMask|keyDownMask, event) || !(event->modifiers & btnState))
  748.             triggered = false;
  749.         else {
  750.             event->where = lastEvent.where;
  751.             if (event->when > lastEvent.when) {
  752.                 d = ((event->when - lastEvent.when) * rate) / 100;
  753.                 if (d == 0)
  754.                     triggered = false;
  755.                 else if (vBar)
  756.                     event->where.v = lastEvent.where.v + d;
  757.                 else if (hBar)
  758.                     event->where.h = lastEvent.where.h + d;
  759.             }
  760.         }
  761.     }
  762.     if (event->when >= lastEvent.when + INTERVAL) {
  763.         if (detached && opt.friction)
  764.             rate = ((100 - FRICTION) * rate) / 100;
  765.         lastEvent = *event;
  766.     }
  767.     bounced = false;
  768. }
  769.  
  770.  
  771. /* ---------- GetNextEvent intercept ---------- */
  772.  
  773.  
  774. /*
  775.  *  myGetNextEvent
  776.  *
  777.  *  This routine is used to implement switching directions while "grabbing".
  778.  *  It's called when the _WaitMouseUp intercept has reported that the mouse
  779.  *  button has been released.  When the application returns to its event loop,
  780.  *  we feed it a fresh click in the scroll bar to get things moving again,
  781.  *  this time in the reverse direction.
  782.  *
  783.  */
  784.  
  785. void
  786. myGetNextEvent(void)
  787. {
  788.     asm {
  789.         btst    #1,13(sp)
  790.         beq.s    @2
  791. ;
  792.         jsr        GetA4
  793.         move.l    a4,-(sp)
  794.         movea.l    (a0),a4
  795.         tst.b    tracking
  796.         beq.s    @1
  797. ;
  798.         movea.l    12(sp),a0
  799.         move.w    #mouseDown,(a0)+        ;  cons up an event
  800.         clr.l    (a0)+
  801.         move.l    Ticks,(a0)+
  802.         move.l    thePoint,(a0)+
  803.         clr.w    (a0)+
  804.         movea.l    (sp)+,a4
  805.         addq.l    #4,sp
  806.         movea.l    (sp)+,a0
  807.         addq.l    #6,sp
  808.         st        (sp)
  809.         jmp        (a0)                    ;  return directly to caller
  810. ;
  811. @1        movea.l    (sp)+,a4                ;  chain on to real _GetNextEvent
  812. @2    }
  813. }
  814.  
  815.  
  816. /* ---------- utility routines ---------- */
  817.  
  818.  
  819. /*
  820.  *  isScrollBar - test whether a control is a scroll bar
  821.  *
  822.  *  This routine is heuristic.  A control is assumed to be a
  823.  *  scroll bar if it uses the system CDEF 1 defproc, or if it
  824.  *  uses a custom defproc and its min/max aren't 0/1.
  825.  *
  826.  *  Controls that are invisible or dimmed are skipped.
  827.  *
  828.  */
  829.  
  830. static Boolean
  831. isScrollBar(ControlHandle cH)
  832. {
  833.     register ControlPtr cP = *cH;
  834.     register struct { long pad; long type; short id; } *p;
  835.     
  836.     if (!cP->contrlVis || cP->contrlHilite || cP->contrlMin >= cP->contrlMax)
  837.         return(false);
  838.     if (cP->contrlValue < cP->contrlMin || cP->contrlValue > cP->contrlMax)
  839.         return(false);
  840.     p = (void *) *cP->contrlDefProc;
  841.     if (p && p->type == 'CDEF') {
  842.         if (p->id == 1)
  843.             return(true);
  844.         if (p->id == 0)
  845.             return(false);
  846.     }
  847.     if (cP->contrlMin == 0 && cP->contrlMax == 1)
  848.         return(false);
  849.     return(true);
  850. }
  851.  
  852.  
  853. /*
  854.  *  localize - convert global to local coordinates
  855.  *
  856.  *  This is somewhat like GlobalToLocal, except it doesn't touch
  857.  *  the current grafport, and it compensates for non-(0,0) origin.
  858.  *
  859.  */
  860.  
  861. static void
  862. localize(Point *where, register WindowPtr wp)
  863. {
  864.     Point origin;
  865.  
  866.     if (wp->portBits.rowBytes < 0)
  867.         origin = topLeft((**((CGrafPtr) wp)->portPixMap).bounds);
  868.     else
  869.         origin = topLeft(wp->portBits.bounds);
  870.     where->v += origin.v - wp->portRect.top;
  871.     where->h += origin.h - wp->portRect.left;
  872. }
  873.  
  874.  
  875. /*
  876.  *  globalize - convert local to global coordinates
  877.  *
  878.  *  This is somewhat like LocalToGlobal, except it doesn't touch
  879.  *  the current grafport, and it compensates for non-(0,0) origin.
  880.  *
  881.  */
  882.  
  883. static void
  884. globalize(Point *where, WindowPtr wp)
  885. {
  886.     Point origin;
  887.  
  888.     if (wp->portBits.rowBytes < 0)
  889.         origin = topLeft((**((CGrafPtr) wp)->portPixMap).bounds);
  890.     else
  891.         origin = topLeft(wp->portBits.bounds);
  892.     where->v -= origin.v - wp->portRect.top;
  893.     where->h -= origin.h - wp->portRect.left;
  894. }
  895.  
  896.  
  897. /*
  898.  *  GetA4 - PC-relative access to A4
  899.  *
  900.  *  The address of the storage used for our A4 is returned in A0.
  901.  *
  902.  */
  903.  
  904. static void
  905. GetA4(void)
  906. {
  907.     asm {
  908.         bsr.s    @1
  909.         dc.l    0            ;  store A4 here
  910. @1        move.l    (sp)+,a0
  911.     }
  912. }
  913.  
  914.  
  915. /* ---------- install trap and lo-mem intercepts ---------- */
  916.  
  917.  
  918. /*
  919.  *  intercept_trap - install intercept for Toolbox or OS trap
  920.  *
  921.  */
  922.  
  923. void
  924. intercept_trap(register short trapno, register struct intercept *p, void *new)
  925. {
  926.     register short type;
  927.  
  928.     if (trapno & 0x0800) {
  929.         type = ToolTrap;
  930.         trapno &= 0x3FF;
  931.     }
  932.     else {
  933.         type = OSTrap;
  934.         trapno &= 0xFF;
  935.     }
  936.     p->jsr = 0x4EB9;
  937.     p->new = new;
  938.     p->jmp = 0x4EF9;
  939.     p->old = (void *) NGetTrapAddress(trapno, type);
  940.     NSetTrapAddress(p, trapno, type);
  941. }
  942.  
  943.  
  944. /*
  945.  *  intercept_lomem - install intercept for lo-mem vector
  946.  *
  947.  */
  948.  
  949. void
  950. intercept_lomem(short v, register struct intercept *p, void *new)
  951. {
  952.     register void **vector = (void **) v;
  953.  
  954.     p->jsr = 0x4EB9;
  955.     p->new = new;
  956.     p->jmp = 0x4EF9;
  957.     p->old = *vector;
  958.     if (!p->old)
  959.         p->jmp = 0x4E75;
  960.     *vector = p;
  961. }
  962.  
  963.  
  964. /* ---------- INIT/cdev communication ---------- */
  965.  
  966.  
  967. /*
  968.  *  install_VBL
  969.  *
  970.  *  We want to allow option changes made in the cdev to affect the INIT
  971.  *  immediately.  (None of this "changes will take effect upon restart"
  972.  *  stuff!)
  973.  *
  974.  *  Therefore we have to leave a pointer to the options record somewhere
  975.  *  where the cdev can find it.  The vertical retrace queue seems like a
  976.  *  good place.
  977.  *
  978.  *  Our VBL code doesn't actually do anything (except make sure it stays
  979.  *  around by resetting the vblCount each time it runs down), but it is
  980.  *  preceded by a signature that enables the cdev to find it by searching
  981.  *  the queue.
  982.  *
  983.  */
  984.  
  985. static void
  986. install_VBL(void)
  987. {
  988.     asm {
  989.         lea        @2,a1
  990.         lea        opt,a0
  991.         move.l    a0,-12(a1)
  992.         lea        @1,a0
  993.         move.l    a1,OFFSET(VBLTask,vblAddr)(a0)
  994.         _VInstall
  995.         return
  996. ;
  997. ;  vbl queue entry
  998. ;
  999. @1        dc.l    0            ;  qLink
  1000.         dc.w    vType        ;  qType
  1001.         dc.l    0            ;  vblAddr
  1002.         dc.w    0x7FFF        ;  vblCount
  1003.         dc.w    0            ;  vblPhase
  1004. ;
  1005. ;  vbl code
  1006. ;
  1007.         dc.l    0            ;  ==> options
  1008.         dc.l    'grab'        ;  signature
  1009.         dc.l    'INIT'        ;  signature
  1010. @2        move.w    #0x7FFF,OFFSET(VBLTask,vblCount)(a0)
  1011.         rts
  1012.     }
  1013. }
  1014.